<template>
    <div className="{className}" style="{style}" ref="{container}" />
</template>
<script>
import React, {useEffect} from 'react';
import G6 from '@antv/g6';
import useGraph from '../../hooks/useGraph';
import useProps from '../../hooks/useProps';
import {
    getGraphSize,
    getGraphId,
    bindSourceMapCollapseEvents,
    processMinimap,
    renderGraph,
    getCommonConfig,
    getArrowCfg,
    bindStateEvents
} from './utils';
import {defaultFlowGraphAnchorPoints, defaultNodeSize, defaultStateStyles, defaultNodeStyle} from '../constants';

export default {
    name: 'FlowAnalysisGraph',
    data() {},
    mounted() {
        const graphs = {};

        const defaultLayout = {
            type: 'dagre',
            rankdir: 'LR',
            center: [0, 0],
            nodesepFunc: () => 1,
            ranksepFunc: () => 1
        };

        const defaultProps = {
            nodeCfg: {
                type: 'indicator-card',
                size: defaultNodeSize,
                style: defaultNodeStyle,
                anchorPoints: defaultFlowGraphAnchorPoints,
                padding: 6,
                layout: 'bundled',
                nodeStateStyles: defaultStateStyles
            },
            edgeCfg: {
                type: 'cubic-horizontal',
                edgeStateStyles: defaultStateStyles
            },
            behaviors: ['zoom-canvas', 'drag-canvas'],
            layout: defaultLayout,
            animate: true,
            markerPosition: 'right',
            autoFit: true,
            fitCenter: true,
            style: {
                height: 'inherit'
            }
        };

        const FlowAnalysisGraph = props => {
            const {
                data,
                className,
                style,
                width,
                height,
                nodeCfg,
                edgeCfg,
                behaviors,
                layout,
                animate,
                minimapCfg,
                autoFit,
                fitCenter,
                markerCfg,
                onReady
            } = props;

            const {
                type: nodeType,
                size: nodeSize,
                anchorPoints: nodeAnchorPoints,
                nodeStateStyles,
                style: nodeStyle,
                title: nodeLabelCfg
            } = nodeCfg || {};

            const {
                type: edgeType,
                style: edgeStyle,
                startArrow: startArrowCfg,
                endArrow: endArrowCfg,
                label: labelCfg,
                edgeStateStyles
            } = edgeCfg || {};
            const container = React.useRef(null);
            const graph = React.useRef(null);
            const graphId = getGraphId(graph);
            useGraph(graphs[graphId], uProps, container);

            useEffect(() => {
                const graphSize = getGraphSize(width, height, container);
                let graph = graphs[graphId];

                if (!graph) {
                    graph = new G6.Graph({
                        container: container.current,
                        width: graphSize[0],
                        height: graphSize[1],
                        animate,
                        modes: {
                            default: behaviors
                        },
                        defaultNode: {
                            type: nodeType,
                            size: nodeSize,
                            anchorPoints: nodeAnchorPoints,
                            nodeCfg
                        },
                        defaultEdge: {
                            type: edgeType,
                            edgeCfg
                        },
                        nodeStateStyles,
                        edgeStateStyles,
                        layout,
                        fitView: autoFit,
                        fitCenter
                    });
                    graphs[graphId] = graph;
                    graph.set('id', graphId);
                }
                // defaultNode 默认只能绑定 plainObject，针对 Function 类型需要通过该模式绑定
                graph.node(node => {
                    if (nodeType === 'indicator-card') {
                        node.markerCfg = markerCfg;
                        return {};
                    }
                    const {style} = nodeLabelCfg;
                    return {
                        label: node.value?.title,
                        labelCfg: {
                            style: getCommonConfig(style, node, graph)
                        },
                        style: {
                            ...(typeof nodeStyle === 'function' ? nodeStyle(node, graph) : nodeStyle)
                        }
                    };
                });
                graph.edge(edge => {
                    const startArrow = getArrowCfg(startArrowCfg, edge);
                    const endArrow = getArrowCfg(endArrowCfg, edge);
                    const {style} = labelCfg || {};
                    return {
                        label: edge.value,
                        labelCfg: {
                            style: getCommonConfig(style, edge, graph)
                        },
                        style: {
                            stroke: '#ccc',
                            startArrow,
                            endArrow,
                            ...(typeof edgeStyle === 'function' ? edgeStyle(edge, graph) : edgeStyle)
                        }
                    };
                });
                processMinimap(minimapCfg, graph);
                bindStateEvents(graph, uProps);
                if (markerCfg) {
                    bindSourceMapCollapseEvents(graph);
                }
                renderGraph(graph, data);
                if (onReady) {
                    onReady(graph);
                }
                return () => {
                    if (graphs[graphId]) {
                        graphs[graphId].destroy();
                        delete graphs[graphId];
                    }
                };
            }, []);

            return <div className={className} style={style} ref={container} />;
        };
        FlowAnalysisGraph(this);
    }
};
</script>
